﻿using ImTools;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using TangGuo.Resources;

namespace TangGuo.UserControls
{
    public static class AStarPath
    {
        static Tile[,] tileMap = new Tile[7, 7];
        public static List<BasePos> FindPath(int[,] map, BasePos fromPos, BasePos toPos)
        {
            if (map == null || fromPos == null || toPos == null) return null;
            for (int i = 0; i < 7; i++)
            {
                for (int j = 0; j < 7; j++)
                {
                    tileMap[i, j] = new();
                    tileMap[i, j].position = oddr_to_cube(i, j);
                    tileMap[i, j].obstracleID = map[i, j];
                    if (j is 1 or 3 or 5)
                    {
                        if (i == 6) tileMap[i, j] = null; // 奇数行最后一个设为Null
                    }
                }
            }

            for (int i = 0; i < 7; i++) // 设置相邻网格，首先是中间区域的网格
            {
                for (int j = 0; j < 7; j++)
                {
                    if (tileMap[i, j] != null)
                    {
                        // 设置相邻网格
                        SetAdjacentPos2D(i, j);
                    }
                }
            }
            List<Tile> list3d = AStarPathFind.FindPath(tileMap[fromPos.Col, fromPos.Row], tileMap[toPos.Col, toPos.Row]);
            List<BasePos> path = new List<BasePos>();
            if (list3d != null && list3d.Count > 0)
            {
                foreach (Tile tile in list3d)
                {
                    BasePos bp = cube_to_oddr(tile.position);
                    path.Add(bp);
                }
            }
            return path;
        }
        /// <summary>
        /// 查找给定网格相邻的与其内容相同的网格，用于合并
        /// </summary>
        /// <param name="fromPos"></param>
        /// <param name="targetPos"></param>
        /// <returns></returns>
        public static List<BasePos> FindSameTile(int[,] map, BasePos targetPos)
        {
            if (map==null || targetPos == null) return null;
            if (tileMap[targetPos.Col, targetPos.Row] == null) return null;
            if (map[targetPos.Col, targetPos.Row] ==0) return null;
            for (int i = 0; i < 7; i++)
            {
                for (int j = 0; j < 7; j++)
                {
                    if (tileMap[i, j] != null)
                    {
                        tileMap[i, j].obstracleID = map[i, j];
                    }
                }
            }
            List<Tile> list3d= AStarPathFind.FindSameTile(tileMap[targetPos.Col, targetPos.Row]);
            List<BasePos> posLIst = new List<BasePos>();
            if (list3d != null && list3d.Count > 0)
            {
                foreach (Tile tile in list3d)
                {
                    BasePos bp = cube_to_oddr(tile.position);
                    posLIst.Add(bp);
                }
            }
            return posLIst;
        }

        /// <summary>
        /// 平面坐标(col,row)映射到六角网格立体坐标(x,y,z)，适用于尖角朝上、奇数行向右偏移的六角网格地图
        /// </summary>
        /// <param name="col"></param>
        /// <param name="row"></param>
        /// <returns></returns>
        public static Vector3D oddr_to_cube(int col, int row)
        {
            var X = col - (row - (row & 1)) / 2;  // 
            var Z = row;
            var Y = -(X + Z);
            return new Vector3D(X, Y, Z);
        }
        /// <summary>
        /// 六角网格立体坐标(x,y,z)映射到平面坐标(col,row)
        /// </summary>
        /// <param name="vector3D"></param>
        /// <returns></returns>
        public static BasePos cube_to_oddr(Vector3D vector3D)
        {
            int X = (int)vector3D.X;
            int Z = (int)vector3D.Z;
            var col = X + (Z - (Z & 1)) / 2;
            var row = Z;
            return new BasePos(col, row);

        }
        public static void SetAdjacentPos2D(int col, int row) // 设置相邻网格
        {
            if (col < 0 || row < 0 || col > 6 || row > 6) return;
            if (tileMap[col, row] == null) return;
            if (row >= 1 && tileMap[col, row - 1] != null) tileMap[col, row].adjacentTiles.Add(tileMap[col, row - 1]);
            if (row <= 5 && tileMap[col, row + 1] != null) tileMap[col, row].adjacentTiles.Add(tileMap[col, row + 1]);
            if (col >= 1 && tileMap[col - 1, row] != null) tileMap[col, row].adjacentTiles.Add(tileMap[col - 1, row]);
            if (col <= 5 && tileMap[col + 1, row] != null) tileMap[col, row].adjacentTiles.Add(tileMap[col + 1, row]);
            if (row % 2 == 0) // 偶数行
            {
                if (col >= 1 && row >= 1 && tileMap[col - 1, row - 1] != null) tileMap[col, row].adjacentTiles.Add(tileMap[col - 1, row - 1]);
                if (col >= 1 && row <= 5 && tileMap[col - 1, row + 1] != null) tileMap[col, row].adjacentTiles.Add(tileMap[col - 1, row + 1]);
            }
            if (row % 2 == 1) // 奇数行
            {
                if (col <= 5 && row >= 1 && tileMap[col + 1, row - 1] != null) tileMap[col, row].adjacentTiles.Add(tileMap[col + 1, row - 1]);
                if (col <= 5 && row <= 5 && tileMap[col + 1, row + 1] != null) tileMap[col, row].adjacentTiles.Add(tileMap[col + 1, row + 1]);
            }
        }
    }

    /// <summary>
    /// 六角网格类
    /// </summary>
    public class Tile
    {
        public int F => g + h; // F权重，本网格到起始地址和目标地址的总的路径成本估值
        public int g; // 到起始地的路径成本估值
        public int h; // 到目的地的路径成本估值
        public Vector3D position; // 网格三维坐标位置
        public List<Tile> adjacentTiles = new List<Tile>(); // 相邻网格列表
        public int obstracleID;  // 障碍物代码，为0时表示没有障碍物
        public bool IsObstacle => obstracleID != 0; // 是否阻挡，本网格有物体时，设为TRUE
    }

    /// <summary>
    /// ASTAR路径算法核心代码
    /// </summary>
    public class AStarPathFind
    {
        public static List<Tile> FindPath(Tile startPoint, Tile endPoint)
        {
            if (startPoint == null || endPoint == null) return null;
            List<Tile> openPathTiles = new List<Tile>(); // 开放列表，用于存放备选网格，一般是与当前网格相邻的网格，但不包括障碍网格，以及已在关闭列表中的网格
            List<Tile> closePathTiles = new List<Tile>(); // 关闭列表，用于存放路径可经过的所有网格
            Tile currentTile = startPoint;
            currentTile.g = 0;
            currentTile.h = GetEstimatePathCost(startPoint.position, endPoint.position);
            openPathTiles.Add(currentTile);
            while (openPathTiles.Count != 0)
            {
                // 对开放列表进行排序，以获得F值最低的那块网格。
                openPathTiles = openPathTiles.OrderBy(x => x.F).ThenByDescending(x => x.g).ToList();
                currentTile = openPathTiles[0];
                openPathTiles.Remove(currentTile);
                closePathTiles.Add(currentTile);
                int g = currentTile.g + 1;
                if (closePathTiles.Contains(endPoint))
                {
                    break; // 如果在关闭列表中有一个目标网格，我们就找到了一个路径。
                }
                foreach (Tile tile in currentTile.adjacentTiles)
                {
                    if (tile.IsObstacle == true) { continue; }  // 忽略不能行走的相邻网格。
                    if (closePathTiles.Contains((Tile)tile)) { continue; }  // 忽略已经在关闭列表中的网格。
                    if (!(openPathTiles.Contains((Tile)tile)))  // 如果它不在开放列表中--添加它并计算G和H。
                    {
                        tile.g = g;
                        tile.h = GetEstimatePathCost(tile.position, endPoint.position);
                        openPathTiles.Add((Tile)tile);

                    }
                    else if (tile.F > g + tile.h)
                    {
                        tile.g = g;  // 检查使用当前的G是否可以得到一个更低的F值，如果可以的话，更新它的值。
                    }
                }
            }

            //回溯--设置最终路径。
            List<Tile> finalPath = new List<Tile>();
            if (closePathTiles.Contains(endPoint))
            {
                currentTile = endPoint;
                finalPath.Add(currentTile);
                for (int i = endPoint.g - 1; i >= 0; i--)
                {
                    currentTile = closePathTiles.Find(x => x.g == i && currentTile.adjacentTiles.Contains(x));
                    finalPath.Add(currentTile);
                }
                finalPath.Reverse();
            }
            return finalPath; // 如果关闭列表中不包含目标网格，此时返回空值，表示没有找到路径
        }
        /// <summary>
        /// 查找给定网格相邻的与其内容相同的网格，用于合并
        /// </summary>
        /// <param name="pos">位置(int Col,int Row)</param>
        /// <param name="id"></param>
        /// <returns></returns>
        public static List<Tile> FindSameTile(Tile targetPos)
        {
            if (targetPos == null) return null;
            List<Tile> openPathTiles = new List<Tile>(); // 开放列表，用于存放备选网格，一般是与当前网格相邻的网格，但不包括障碍网格，以及已在关闭列表中的网格
            List<Tile> closePathTiles = new List<Tile>(); // 关闭列表，用于存放路径可经过的所有网格
            targetPos.h = 0;
            openPathTiles.Add(targetPos);
            Tile currentTile;
            while (openPathTiles.Count != 0)
            {
                openPathTiles = openPathTiles.OrderBy(x => x.h).ToList(); // 按距离升序排序
                currentTile = openPathTiles[0];
                openPathTiles.Remove(currentTile);
                closePathTiles.Add(currentTile);
                foreach (Tile tile in currentTile.adjacentTiles)
                {
                    if (tile.IsObstacle == false || tile.obstracleID!=targetPos.obstracleID) { continue; }  // 忽略空网格，忽略障碍物编号不相同的网格。
                    if (closePathTiles.Contains((Tile)tile)) { continue; }  // 忽略已经在关闭列表中的网格。
                    if (!(openPathTiles.Contains((Tile)tile)))  // 如果它不在开放列表中--添加它并计算G和H。
                    {
                        tile.h = GetEstimatePathCost(tile.position, targetPos.position);
                        openPathTiles.Add((Tile)tile);
                    }
                }
            }
            if (closePathTiles.Contains(targetPos)) { closePathTiles.Remove((Tile)targetPos); }
            closePathTiles= closePathTiles.OrderBy(x => x.h).ToList(); // 按距离升序排序
            closePathTiles.Reverse(); // 逆序
            return closePathTiles;

        }
        /// <summary>
        /// 使用曼哈顿算法 。从给定的起始位置到六角网格的目标位置路径的估计成本
        /// </summary>
        /// <param name="startPosition">起始位置</param>
        /// <param name="targetPosition">目标位置</param>
        /// <returns></returns>
        private static int GetEstimatePathCost(Vector3D startPosition, Vector3D targetPosition)
        {
            return (int)MathF.Max(
                MathF.Abs((float)(startPosition.Z - targetPosition.Z)),
                MathF.Max(MathF.Abs((float)(startPosition.X - targetPosition.X)), MathF.Abs((float)(startPosition.Y - targetPosition.Y)))
                );
        }
    }

}
